package com.owulia.leetcode.texas;

import com.alibaba.fastjson2.JSON;

import java.util.*;

public class TexasHoldem {

    public static final int MAX_RANK = 15;

    public static void main(String[] args) {
        // public static final String[] SUITS = {"♠", "♥", "♦", "♣"};


        // 输入两张手牌
        String hand1 = "♠2";
        String hand2 = "♠Q";
        System.out.println("两张手牌 " + hand1 + " " + hand2);

        // 输入五张公共牌
        String[] communityCards = {"♠K", "♠J", "♦Q", "♥2", "♠A"};
        System.out.println("五张公共牌 " + JSON.toJSONString(communityCards));
        // 计算最佳牌型
        String[] allCards = {hand1, hand2, communityCards[0], communityCards[1], communityCards[2], communityCards[3], communityCards[4]};
        String[] bestHand = findBestHand(allCards);

        // 输出最佳牌型
        System.out.println("您的最佳牌型为：" + Arrays.toString(bestHand));
        System.out.println("您的最佳牌型点数为：" + evaluateHand(bestHand));
    }

    // 找到最佳牌型
    public static String[] findBestHand(String[] allCards) {
        String[][] combinations = generateCombinations(allCards, 5);
        String[] bestHand = combinations[0];

        for (int i = 1; i < combinations.length; i++) {
            if (compareHands(combinations[i], bestHand) > 0) {
                bestHand = combinations[i];
            }
        }

        return bestHand;
    }

    // 生成所有可能的组合
    public static String[][] generateCombinations(String[] allCards, int n) {
        int numCombinations = factorial(allCards.length) / (factorial(n) * factorial(allCards.length - n));
        String[][] combinations = new String[numCombinations][n];
        int[] indices = new int[n];
        for (int i = 0; i < n; i++) {
            indices[i] = i;
        }

        for (int i = 0; i < numCombinations; i++) {
            for (int j = 0; j < n; j++) {
                combinations[i][j] = allCards[indices[j]];
            }

            int j = n - 1;
            while (j >= 0 && indices[j] == allCards.length - n + j) {
                j--;
            }

            if (j >= 0) {
                indices[j]++;
                for (int k = j + 1; k < n; k++) {
                    indices[k] = indices[k - 1] + 1;
                }
            }
        }

        return combinations;
    }

    // 比较两个牌型的大小
    public static int compareHands(String[] hand1, String[] hand2) {
        int value1 = evaluateHand(hand1);
        System.out.println(JSON.toJSONString(hand1) + "->" + value1);
        int value2 = evaluateHand(hand2);
        System.out.println(JSON.toJSONString(hand2) + "->" + value2);

        if (value1 > value2) {
            return 1;
        } else if (value1 < value2) {
            return -1;
        } else {
            return compareHighCards(hand1, hand2);
        }
    }

    // 评估牌型的价值
    public static int evaluateHand(String[] hand) {
        Arrays.sort(hand);
        List<String> handList = Arrays.asList(hand);
        handList.sort(Comparator.comparingInt(TexasHoldem::rankCard));
        hand = (String[]) handList.toArray();
        Map<String, Integer> counts = countRanks(hand);
        boolean flush = isFlush(hand);
        boolean straight = isStraight(hand);
        if (flush && straight) { // 同花顺
            return 9000000 + rankCard(hand[4]);
        } else if (counts.containsValue(4)) { // 四条
            int rank = getValueRankFromMap(counts, 4);
            String[] unUseHand = unUseHand(hand, rank, 0);
            return 8000000 + rank * MAX_RANK + rankCard(unUseHand[0]);
        } else if (counts.containsValue(3) && counts.containsValue(2)) { // 葫芦
            int rank3 = getValueRankFromMap(counts, 3);
            int rank2 = getValueRankFromMap(counts, 2);
            return 7000000 + rank3 * MAX_RANK + rank2;
        } else if (flush) { // 同花
            return 6000000 + highRank(hand);
        } else if (straight) { // 顺子
            return 5000000 + rankCard(hand[4]);
        } else if (counts.containsValue(3)) { // 三条
            int rank3 = getValueRankFromMap(counts, 3);
            String[] unUseHand = unUseHand(hand, rank3, 0);
            return 4000000 + rank3 * MAX_RANK * MAX_RANK + rankCard(unUseHand[1]) * MAX_RANK + rankCard(hand[0]);
        } else if (counts.containsValue(2)) { // 对子
            int rank1 = getValueRankFromMap(counts, 2);
            int rank2 = getValueRankFromMap(counts, 2);
            String[] unUseHand = unUseHand(hand, rank1, rank2);
            if (rank2 > 0) {
                return 3000000 + Math.max(rank1, rank2) * MAX_RANK * MAX_RANK + Math.min(rank1, rank2) * MAX_RANK + rankCard(unUseHand[0]);
            } else {
                return 2000000 + Math.max(rank1, rank2) * MAX_RANK * MAX_RANK * MAX_RANK + rankCard(unUseHand[2]) * MAX_RANK * MAX_RANK + rankCard(unUseHand[1]) * MAX_RANK + rankCard(unUseHand[0]);
            }
        } else { // 散牌
            return 1000000 + highRank(hand);
        }
    }


    public static String[] unUseHand(String[] hand, int rank1, int rank2) {
        String[] unUseHand = new String[5];
        int index = 0;
        for (String s : hand) {
            int rank = rankCard(s);
            if (rank != rank1 && rank != rank2) {
                unUseHand[index++] = s;
            }
        }
        return unUseHand;
    }


    private static int highRank(String[] hand) {
        return rankCard(hand[4]) * MAX_RANK * MAX_RANK * MAX_RANK * MAX_RANK +
                rankCard(hand[3]) * MAX_RANK * MAX_RANK * MAX_RANK +
                rankCard(hand[2]) * MAX_RANK * MAX_RANK +
                rankCard(hand[1]) * MAX_RANK +
                rankCard(hand[0]);
    }

    // 计算牌的等级
    public static int rankCard(String card) {
        String rank = card.substring(1, 2);
        return rankPoint(rank);
    }

    public static int rankPoint(String rank) {
        if (null == rank || Objects.equals(rank, "")) {
            return 0;
        }
        switch (rank) {
            case "A":
                return 14;
            case "K":
                return 13;
            case "Q":
                return 12;
            case "J":
                return 11;
            default:
                return Integer.parseInt(rank);
        }
    }

    // 比较高牌
    public static int compareHighCards(String[] hand1, String[] hand2) {
        for (int i = 4; i >= 0; i--) {
            int rank1 = rankCard(hand1[i]);
            int rank2 = rankCard(hand2[i]);

            if (rank1 > rank2) {
                return 1;
            } else if (rank1 < rank2) {
                return -1;
            }
        }

        return 0;
    }

    // 判断是否同花
    public static boolean isFlush(String[] hand) {
        char suit = hand[0].charAt(0);

        for (int i = 1; i < 5; i++) {
            if (hand[i].charAt(0) != suit) {
                return false;
            }
        }

        return true;
    }

    // 判断是否顺子
    public static boolean isStraight(String[] hand) {
        int[] ranks = new int[5];

        for (int i = 0; i < 5; i++) {
            ranks[i] = rankCard(hand[i]);
        }

        Arrays.sort(ranks);

        if (ranks[0] == 0 && ranks[1] == 1 && ranks[2] == 2 && ranks[3] == 3 && ranks[4] == 12) {
            return true;
        }

        for (int i = 0; i < 4; i++) {
            if (ranks[i] + 1 != ranks[i + 1]) {
                return false;
            }
        }

        return true;
    }

    // 计算排名数量
    public static Map<String, Integer> countRanks(String[] hand) {
        Map<String, Integer> counts = new HashMap<>();

        for (int i = 0; i < 5; i++) {
            String rank = hand[i].substring(1, 2);
            counts.put(rank, counts.getOrDefault(rank, 0) + 1);
        }

        return counts;
    }

    // 获取键对应的值
    public static int getValueRankFromMap(Map<String, Integer> map, int value) {
        String result = "";
        for (String key : map.keySet()) {
            if (map.get(key) == value) {
                result = key;
                break;
            }
        }
        map.remove(result);
        return rankPoint(result);
    }

    // 计算阶乘
    public static int factorial(int n) {
        int result = 1;

        for (int i = 2; i <= n; i++) {
            result *= i;
        }

        return result;
    }
}
